package com.lhy.common.web.util;


import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.lang.reflect.*;
import java.util.*;

/**
 * Copyright: Copyright (c) 2015 zznode
 *
 * @ClassName: ReflectionUtils.java
 */
public class ReflectionUtils {

    ReflectionUtils() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtils.class);

    public static void setFieldValue(Object object, String fieldName,
                                     Object value) {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field ["
                    + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);
        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            LOGGER.error("", e);
        }
    }

    public static Object getFieldValue(Object object, String fieldName) {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field ["
                    + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);

        Object result = null;
        try {
            result = field.get(object);
        } catch (IllegalAccessException e) {
            LOGGER.error("", e);
        }
        return result;
    }

    public static Object invokeMethod(Object object, String methodName,
                                      Class<?>[] parameterTypes, Object[] parameters)
            throws InvocationTargetException {
        Method method = getDeclaredMethod(object, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method ["
                    + methodName + "] on target [" + object + "]");
        }
        method.setAccessible(true);
        try {
            return method.invoke(object, parameters);
        } catch (IllegalAccessException e) {
            LOGGER.error("", e);
        }
        return null;
    }

    protected static Field getDeclaredField(Object object, String fieldName) {
        for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass
                .getSuperclass())
            try {
                return superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                LOGGER.error("", e);
            }
        return null;
    }

    /**
     * @param object
     * @return
     * @Function: ReflectionUtils::getAllDeclaredField
     * @Description: ��ø�������и�����ֶΰ���˽���ֶ�
     * @version: v1.0.0
     * @author: Administrator
     * @date: 2015��4��3�� ����10:04:07
     * <p>
     * Modification History:
     * Date         Author          Version            Description
     * -------------------------------------------------------------
     */
    public static Field[] getAllDeclaredField(Object object) {
        return getAllDeclaredField(object, true);
    }

    public static Field[] getAllDeclaredField(Object object, boolean noStatic) {
        List<Field> l = new ArrayList<Field>();
        for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass
                .getSuperclass())
            try {
                Field[] declaredFields = superClass.getDeclaredFields();
                for (Field field : declaredFields) {
                    if (noStatic) {
                        if (!Modifier.isStatic(field.getModifiers())) {
                            l.add(field);
                        }
                    } else {
                        l.add(field);
                    }
                }
            } catch (Exception e) {
                LOGGER.error("", e);
            }
        return l.toArray(new Field[0]);
    }

    protected static void makeAccessible(Field field) {
        if ((!(Modifier.isPublic(field.getModifiers())))
                || (!(Modifier.isPublic(field.getDeclaringClass()
                .getModifiers()))))
            field.setAccessible(true);
    }

    public static Method getDeclaredMethod(Object object, String methodName,
                                           Class<?>[] parameterTypes) {
        for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass
                .getSuperclass())
            try {
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                LOGGER.error("", e);
            }
        return null;
    }

    public static Method getDeclaredMethod(Class clazz, String methodName,
                                           Class<?>[] parameterTypes) {
        for (Class superClass = clazz; superClass != Object.class; superClass = superClass
                .getSuperclass())
            try {
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                LOGGER.error("", e);
            }
        return null;
    }

    public static <T> Class<T> getSuperClassGenricType(Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    public static Class getSuperClassGenricType(Class clazz, int index) {
        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType)) {
            LOGGER.warn(clazz.getSimpleName()
                    + "'s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if ((index >= params.length) || (index < 0)) {
            LOGGER.warn("Index: " + index + ", Size of "
                    + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);

            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            LOGGER
                    .warn(clazz.getSimpleName()
                            + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class) params[index];
    }

    public static IllegalArgumentException convertToUncheckedException(
            Exception e) {
        if ((e instanceof IllegalAccessException)
                || (e instanceof IllegalArgumentException)
                || (e instanceof NoSuchMethodException)) {
            return new IllegalArgumentException("Refelction Exception.", e);
        }
        return new IllegalArgumentException(e);
    }

    public static Map<String, Object> getEntityColumns(Object entity) {
        return Arrays.stream(entity.getClass().getDeclaredFields()).filter(field -> !Modifier.isStatic(field.getModifiers()))
                .collect(HashMap::new,
                        (m, field) -> m.put(getColumnName(field),
                                ReflectionUtils.getFieldValue(entity, field.getName())), HashMap::putAll);
    }

    private static String getColumnName(Field field) {
        if (field.getAnnotation(TableId.class) != null) {
            return field.getAnnotation(TableId.class).value();
        }
        if (field.getAnnotation(TableField.class) != null) {
            return field.getAnnotation(TableField.class).value();
        }
        return field.getName();
    }


    public static Class getSuperClass(Class clazz) {
        Class superclass = clazz.getSuperclass();
        if (superclass != Object.class) {
            return getSuperClass(superclass);
        }
        return clazz;
    }

    public static Field getTableIdField(Class aClass) {
        Map<String, Field> mp = new HashMap<>(1);
        org.springframework.util.ReflectionUtils.doWithFields(aClass, field -> {
            boolean b = field.getAnnotation(TableId.class) != null && !Modifier.isStatic(field.getModifiers());
            if (b) {
                mp.put("field", field);
            }
        });
        Assert.notNull(mp, "未找到主键字段");
        return mp.get("field");
    }

    /**
     * 获取类的泛型参数
     *
     * @param clazz
     * @param index
     * @param <T>
     * @return
     */
    public static <T> Class<T> getParameterizedType(Class<?> clazz, int index) {
        return (Class<T>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[index];
    }
}
